package cn.hyatt.code.generator.service.impl;

import cn.hyatt.code.generator.data.ColumnItem;
import cn.hyatt.code.generator.data.TableItem;
import cn.hyatt.code.generator.dto.CodeGeneratorConfig;
import cn.hyatt.code.generator.entity.Column;
import cn.hyatt.code.generator.service.CodeGeneratorService;
import cn.hyatt.code.generator.service.ColumnService;
import cn.hyatt.code.generator.utils.DataTypeUtil;
import cn.hyatt.common.exception.BusinessException;
import cn.hyatt.common.utils.StringTool;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;

import javax.sql.DataSource;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Slf4j
@Service
@Primary
@AllArgsConstructor
public class CodeGeneratorServiceImpl implements CodeGeneratorService {

    private ColumnService columnService;
    private DataSource dataSource;


    @Override
    public void generatorZip(CodeGeneratorConfig config, ZipOutputStream zipOutputStream) throws IOException {
        Map<String, Object> map = getTemplateInfo(config);
        List<String> templateList = getTemplateList();

        //设置velocity资源加载器
        Properties prop = new Properties();
        prop.put("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
        Velocity.init(prop);

        //
        String dtoPackageName = (String) map.get("dtoPackageName");
        TableItem tableItem = (TableItem) map.get("tableInfo");
        String tableName = tableItem.getTableName();
        VelocityContext velocityContext = new VelocityContext(map);


        for (String templatePath : templateList) {
            String filePath = getFilePath(templatePath, dtoPackageName, tableName);
            Template template = Velocity.getTemplate(templatePath, "UTF-8");
            StringWriter sw = new StringWriter();
            template.merge(velocityContext, sw);
            zipOutputStream.putNextEntry(new ZipEntry(filePath));
            IOUtils.write(sw.toString(), zipOutputStream, "UTF-8");
            IOUtils.close(sw);
            zipOutputStream.closeEntry();
        }

    }

    private String getConnectionDatabaseName() {
        Connection connection = null;
        try {
            connection = dataSource.getConnection();
            return connection.getCatalog();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }

    }


    private Map<String, Object> getTemplateInfo(CodeGeneratorConfig config) {
        String databaseName = getConnectionDatabaseName();
        // 获取表名字段列表
        List<Column> columnList = columnService.findAllByTableName(databaseName, config.getTableName());
        if (columnList.isEmpty()) {
            throw new BusinessException("无法根据表名(" + config.getTableName() + ")生成,因为找不到对应字段信息。");
        }

        // 删除表前缀
        String removePrefixTableName = StringTool.removePrefix(config.getTableName(), config.getTablePrefix());
        // 表名-转大驼峰命名
        String tableName = StringTool.strToUpperCamel(removePrefixTableName, "_");
        String dtoPackageName = StringTool.strToCamel(removePrefixTableName, "_");


        // 表信息
        TableItem tableItem = TableItem
                .builder()
                .oldTableName(config.getTableName())
                .tableName(tableName)
                .tableRemark(config.getTableRemark())
                .build();

        // 列信息
        List<ColumnItem> columnItemList = new ArrayList<>();
        for (Column column : columnList) {
            ColumnItem columnItem = ColumnItem.builder()
                    .oldColumnName(column.getColumnName())
                    .columnName(StringTool.strToCamel(column.getColumnName(), "_"))
                    .columnRemark(column.getColumnComment())
                    .columnDataType(DataTypeUtil.mysqlDataTypeToJavaDataType(column.getDataType()))
                    .isNullable("YES".equals(column.getIsNullable()))
                    .build();
            columnItemList.add(columnItem);
        }

        Map<String, Object> map = new HashMap<>();
        map.put("author", config.getAuthor());
        map.put("package", config.getPackagePath());
        map.put("dtoPackageName", dtoPackageName);
        map.put("uri", config.getUri());
        map.put("tableInfo", tableItem);
        map.put("columnList", columnItemList);
        return map;
    }

    private List<String> getTemplateList() {

        return List.of(
                "codeTemplate/controller/Controller.java.vm",
                "codeTemplate/dto/FindDto.java.vm",
                "codeTemplate/dto/SaveDto.java.vm",
                "codeTemplate/dto/UpdateDto.java.vm",
                "codeTemplate/dto/Vo.java.vm",
                "codeTemplate/entity/Entity.java.vm",
                "codeTemplate/mapper/Mapper.java.vm",
                "codeTemplate/mapper/Mapper.xml.vm",
                "codeTemplate/service/Service.java.vm",
                "codeTemplate/service/impl/ServiceImpl.java.vm"
        );
    }

    private String getFilePath(String templatePath, String dtoPackageName, String className) {
        // 分隔符
        String separator = File.separator;
        if (templatePath.endsWith("Controller.java.vm")) {
            return "code" + separator + "java" + separator + "controller" + separator + className + "Controller.java";
        } else if (templatePath.endsWith("FindDto.java.vm")) {
            return "code" + separator + "java" + separator + "dto" + separator + dtoPackageName + separator + className + "FindDto.java";
        } else if (templatePath.endsWith("SaveDto.java.vm")) {
            return "code" + separator + "java" + separator + "dto" + separator + dtoPackageName + separator + className + "SaveDto.java";
        } else if (templatePath.endsWith("UpdateDto.java.vm")) {
            return "code" + separator + "java" + separator + "dto" + separator + dtoPackageName + separator + className + "UpdateDto.java";
        } else if (templatePath.endsWith("Vo.java.vm")) {
            return "code" + separator + "java" + separator + "dto" + separator + dtoPackageName + separator + className + "Vo.java";
        } else if (templatePath.endsWith("Entity.java.vm")) {
            return "code" + separator + "java" + separator + "entity" + separator + className + ".java";
        } else if (templatePath.endsWith("Mapper.java.vm")) {
            return "code" + separator + "java" + separator + "mapper" + separator + className + "Mapper.java";
        } else if (templatePath.endsWith("Mapper.xml.vm")) {
            return "code" + separator + "java" + separator + "mapper" + separator + className + "Mapper.xml";
        } else if (templatePath.endsWith("Service.java.vm")) {
            return "code" + separator + "java" + separator + "service" + separator + className + "Service.java";
        } else if (templatePath.endsWith("ServiceImpl.java.vm")) {
            return "code" + separator + "java" + separator + "service" + separator + "impl" + separator + className + "ServiceImpl.java";
        }

        return "";
    }

}
